/**
 * @file		retarget.c
 * @brief		retarget
 * @note		None
 * @attention	None
 * 
 * <B><I>ALL RIGHTS RESERVED, COPYRIGHT&copy; SOCIONEXT INCORPORATED 2016</I></B>
 */
#define MALLOC_LOCK_CTRL_ON		// Newlib nano may NOT support ?!
#define ENV_LOCK_CTRL_ON
#define TZ_LOCK_CTRL_ON

/* Standard includes. */
#include <malloc.h>

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "semphr.h"
#include "ARMCM0.h"

#include "user_custom.h"

#include "ipcu.h"
#include "uart.h"
#include "dd_pmc_uart.h"
#include "dd_pmc_exiu.h"

#ifdef MALLOC_LOCK_CTRL_ON
static SemaphoreHandle_t xMutexMalloc = NULL;
#endif /* MALLOC_LOCK_CTRL_ON */

#ifdef ENV_LOCK_CTRL_ON
static SemaphoreHandle_t xMutexEnv = NULL;
#endif /* ENV_LOCK_CTRL_ON */

#ifdef TZ_LOCK_CTRL_ON
static SemaphoreHandle_t xMutexTz = NULL;
#endif /* TZ_LOCK_CTRL_ON */

extern unsigned custom_EXIU_Set_Init( void );

void retarget_init_before_main()
{
  // Set Interrupt Priority.
  NVIC_SetPriority(HDMAC0_IRQn, 3);
  NVIC_SetPriority(HDMAC1_IRQn, 3);
  NVIC_SetPriority(HDMAC2_IRQn, 3);
  NVIC_SetPriority(HDMAC3_IRQn, 3);
  NVIC_SetPriority(HDMAC4_IRQn, 3);
  NVIC_SetPriority(UART_Rx_IRQn, 3);
  NVIC_SetPriority(UART_Tx_IRQn, 3);
  NVIC_SetPriority(SPI_IRQn, 3);
  NVIC_SetPriority(Calendar_IRQn, 3);
  NVIC_SetPriority(TIMER32_IRQn, 3);
  NVIC_SetPriority(ADC_IRQn, 3);
  NVIC_SetPriority(I2C_IRQn, 3);
  NVIC_SetPriority(WATCHDOG_IRQn, 3);
  NVIC_SetPriority(TS_IF_IRQn, 3);
  NVIC_SetPriority(CTC_IRQn, 3);
  NVIC_SetPriority(IPCU0_IRQn, 3);
  NVIC_SetPriority(IPCU1_IRQn, 3);
  NVIC_SetPriority(IPCU2_IRQn, 3);
  NVIC_SetPriority(IPCU3_IRQn, 3);
  NVIC_SetPriority(GPIO_IRQn, 3);
  // Interrupt enable.
  NVIC_EnableIRQ(HDMAC0_IRQn);
  NVIC_EnableIRQ(HDMAC1_IRQn);
  NVIC_EnableIRQ(HDMAC2_IRQn);
  NVIC_EnableIRQ(HDMAC3_IRQn);
  NVIC_EnableIRQ(HDMAC4_IRQn);
  NVIC_EnableIRQ(UART_Rx_IRQn);
  NVIC_EnableIRQ(UART_Tx_IRQn);
  NVIC_EnableIRQ(SPI_IRQn);
  NVIC_EnableIRQ(Calendar_IRQn);
  NVIC_EnableIRQ(TIMER32_IRQn);
  NVIC_EnableIRQ(ADC_IRQn);
  NVIC_EnableIRQ(I2C_IRQn);
  NVIC_EnableIRQ(WATCHDOG_IRQn);
  NVIC_EnableIRQ(TS_IF_IRQn);
  NVIC_EnableIRQ(CTC_IRQn);
  NVIC_EnableIRQ(IPCU0_IRQn);
  NVIC_EnableIRQ(IPCU1_IRQn);
  NVIC_EnableIRQ(IPCU2_IRQn);
  NVIC_EnableIRQ(IPCU3_IRQn);
#ifdef BOARD_TYPE_SNI_SMALL
  NVIC_EnableIRQ(GPIO_IRQn);
#endif	// BOARD_TYPE_SNI_SMALL

  // Set EXIU
#if defined(BOARD_TYPE_SNI)
  // ch15
  Dd_PMC_EXIU_Set_EIEDG_Edge(15);			// Edge
  
  Dd_PMC_EXIU_Set_EIMASK_Val(0x0001FFFF);	// EIMASK0~15:1(mask)
#elif defined(BOARD_TYPE_SNI_SMALL)
  if(custom_EXIU_Set_Init() == FALSE){
	// ch0
	Dd_PMC_EXIU_Set_EIEDG_Edge(0);			// Edge
	Dd_PMC_EXIU_Set_EILVL_Lo(0);				// Falling Edge.
	Dd_PMC_EXIU_Set_EIREQCLR_Ch(0);			// Clear Interrupt Request.
	// ch15
	Dd_PMC_EXIU_Set_EIEDG_Edge(15); 		  // Edge
  	
	Dd_PMC_EXIU_Set_EIMASK_Val(0x0001FFFE);   // EIMASK0:0(mask release)
  }
#endif	// BOARD_TYPE_SNI || BOARD_TYPE_SNI_SMALL

  __enable_irq();

  // Initialize UART
  vUARTInit();
  // Initialize IPCU
  vIPCUInit();
}

int _write (int fd, char *ptr, int len)
{
  /* Write "len" of char from "ptr" to file id "fd"
   * Return number of char written.
   * Need implementing with UART here. */
  iUARTOutputTerminal(ptr, len);
  
  return len;
}

int _read (int fd, char *ptr, int len)
{
  /* Read "len" of char to "ptr" from file id "fd"
   * Return number of char read.
   * Need implementing with UART here. */
  return len;
}

void _ttywrch(int ch) {
  /* Write one char "ch" to the default console
   * Need implementing with UART here. */
}

void retarget_init_on_RTOS()
{
#ifdef MALLOC_LOCK_CTRL_ON
	xMutexMalloc = xSemaphoreCreateRecursiveMutex();
#endif /* MALLOC_LOCK_CTRL_ON */

#ifdef ENV_LOCK_CTRL_ON
	xMutexEnv = xSemaphoreCreateRecursiveMutex();
#endif /* ENV_LOCK_CTRL_ON */

#ifdef TZ_LOCK_CTRL_ON
	xMutexTz = xSemaphoreCreateRecursiveMutex();
#endif /* TZ_LOCK_CTRL_ON */
}

#ifdef MALLOC_LOCK_CTRL_ON
void __malloc_lock(struct _reent *reent)
{
	if(NULL != xMutexMalloc) {
		xSemaphoreTakeRecursive( xMutexMalloc, portMAX_DELAY );
	}
}

void __malloc_unlock (struct _reent *reent)
{
	if(NULL != xMutexMalloc) {
		xSemaphoreGiveRecursive( xMutexMalloc );
	}
}
#endif /* MALLOC_LOCK_CTRL_ON */

#ifdef ENV_LOCK_CTRL_ON
void __env_lock(struct _reent *reent)
{
	if(NULL != xMutexEnv) {
		xSemaphoreTakeRecursive( xMutexEnv, portMAX_DELAY );
	}
}

void __env_unlock (struct _reent *reent)
{
	if(NULL != xMutexEnv) {
		xSemaphoreGiveRecursive( xMutexEnv );
	}
}
#endif /* ENV_LOCK_CTRL_ON */

#ifdef TZ_LOCK_CTRL_ON
void __tz_lock(struct _reent *reent)
{
	if(NULL != xMutexTz) {
		xSemaphoreTakeRecursive( xMutexTz, portMAX_DELAY );
	}
}

void __tz_unlock (struct _reent *reent)
{
	if(NULL != xMutexTz) {
		xSemaphoreGiveRecursive( xMutexTz );
	}
}
#endif /* TZ_LOCK_CTRL_ON */

#if 1

#include <string.h>
#include <stdlib.h>
#include "FreeRTOS.h"
#include "task.h"

/* definition of block structure, copied from heap2 allocator */
typedef struct A_BLOCK_LINK
{
	struct A_BLOCK_LINK *pxNextFreeBlock;	/*<< The next free block in the list. */
	size_t xBlockSize;						/*<< The size of the free block. */
} xBlockLink;

static const unsigned short  heapSTRUCT_SIZE	= ( sizeof( xBlockLink ) + portBYTE_ALIGNMENT - ( sizeof( xBlockLink ) % portBYTE_ALIGNMENT ) );

_PTR _realloc_r(struct _reent *re, _PTR oldAddr, size_t newSize) {
	xBlockLink *block;
	size_t toCopy;
	void *newAddr;

	newAddr = pvPortMalloc(newSize);

	if (newAddr == NULL)
		return NULL;

	/* We need the block struct pointer to get the current size */
	block = oldAddr;
	block -= heapSTRUCT_SIZE;

	/* determine the size to be copied */
	toCopy = (newSize<block->xBlockSize)?(newSize):(block->xBlockSize);

	/* copy old block into new one */
	memcpy((void *)newAddr, (void *)oldAddr, (size_t)toCopy);

	vPortFree(oldAddr);
	
	return newAddr;
}

_PTR _calloc_r(struct _reent *re, size_t num, size_t size) {
	return pvPortMalloc(num*size);
}

_PTR _malloc_r(struct _reent *re, size_t size) {
	return pvPortMalloc(size);
}

_VOID _free_r(struct _reent *re, _PTR ptr) {
	vPortFree(ptr);
}

#endif
